// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2018-2019 Ariadne Devos
/* sHT -- test index masking */
/* Derived from <tests/inc-saturated.c>

  This is important to test, because it is mostly only used for
  during a speculative execution -- where most of the results
  are thrown away, except for some side-channels. */

#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sHT/nospec.h>

#ifdef SIZE_C
#elif SIZE_MAX == UINT16_MAX
#	define SIZE_C(a) UINT16_C(a)
#elif SIZE_MAX == UINT32_MAX
#	define SIZE_C(a) UINT32_C(a)
#elif SIZE_MAX == UINT64_MAX
#	define SIZE_C(a) UINT64_C(a)
#elif SIZE_MAX == UINT128_MAX
#	define SIZE_C(a) UINT128_C(a)
#elif
#	error unsupported architecture
#endif

static void
test(size_t old_i, size_t n)
{
	size_t i = sHT_index_nospec(old_i, n);
	if (n == 0) {
		if (i == 0)
			return;
		goto error;
	}
	if (old_i < n) {
		if (i == old_i)
			return;
		goto error;
	}
	/* old_i >= n, n != 0 */

	if (i >= n)
		goto error;
	return;

error:
	if (puts("FAIL: sHT_index_nospec") < 0)
		exit(2);
	exit(1);
}

int
main(void)
{

#define HALFWORK SIZE_C(1024)

	/* Don't use sHT_index_iterate, as that would be create a rather
	  circular situation.

	  Exploit semantics of wrap-around arithmetic for easy high and low
	  values, without 2^128 iterations. */
	for (size_t n = SIZE_MAX - HALFWORK; n != HALFWORK; n++) {
		for (size_t i = SIZE_MAX - HALFWORK; i != HALFWORK; i++) {
			test(i, n);
			test(i + SIZE_MAX/2, n);
			test(i + SIZE_MAX/2, n + SIZE_MAX/2);
			test(i, n + SIZE_MAX/2);
		}
	}
	if (puts("PASS: sHT/index_nospec") < 0)
		exit(2);
	exit(0);
}
